/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <interrupts.h>
#include <cpu.h>
#include <vm.h>
#include <bitmap.h>

/****************************************************************************
 **************************   变量声明   ************************************
 ****************************************************************************/
irq_handler_t interrupt_handlers[MAX_INTERRUPTS];

/* TODO: 什么用处？ */
BITMAP_ALLOC(hyp_interrupt_bitmap, MAX_INTERRUPTS);
BITMAP_ALLOC(global_interrupt_bitmap, MAX_INTERRUPTS);

/****************************************************************************
 ************************** 公共函数定义 ************************************
 ****************************************************************************/
/**
 * 功能描述: 注册中断处理程序
 * 入口参数: int_id:    中断ID
 *           handler:   中断处理程序
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void interrupts_reserve(irqid_t int_id, irq_handler_t handler)
{
    if (int_id < MAX_INTERRUPTS) {
        interrupt_handlers[int_id] = handler;
        bitmap_set(hyp_interrupt_bitmap, int_id);
        bitmap_set(global_interrupt_bitmap, int_id);
    }
}

/**
 * 功能描述: 初始化中断
 *           (1) 初始化与硬件架构相关部分内容
 *           (2) 注册核间中断，用于在CPU核间传递消息
 *           (3) 使能核间中断
 * 入口参数: void
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline void interrupts_init(void)
{
    interrupts_arch_init();

    if (cpu.id == CPU_MASTER) {
        interrupts_reserve(IPI_CPU_MSG, cpu_msg_handler);
    }

    interrupts_cpu_enable(IPI_CPU_MSG, true);
}

/**
 * 功能描述: 使能某个中断
 * 入口参数: int_id:    中断ID
 *           en:        使能位
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline void interrupts_cpu_enable(irqid_t int_id, bool en)
{
    interrupts_arch_enable(int_id, en);
}

/**
 * 功能描述: 注册中断处理程序
 * 入口参数: int_id:    中断ID
 *           handler:   中断处理程序
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline bool interrupts_check(irqid_t int_id)
{
    return interrupts_arch_check(int_id);
}

/**
 * 功能描述: 注册中断处理程序
 * 入口参数: int_id:    中断ID
 *           handler:   中断处理程序
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline void interrupts_clear(irqid_t int_id)
{
    interrupts_arch_clear(int_id);
}

/**
 * 功能描述: 通过核间中断发送消息
 * 入口参数: target_cpu,目标核
 *           ipi_id,    核间中断号
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline void interrupts_cpu_sendipi(cpuid_t target_cpu, irqid_t ipi_id)
{
    interrupts_arch_ipi_send(target_cpu, ipi_id);
}

/**
 * 功能描述: 将中断注入到VM中
 * 入口参数: vm,    目标VM
 *           id,    中断号
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
inline void interrupts_vm_inject(struct vm *vm, irqid_t id)
{
    interrupts_arch_vm_inject(vm, id);
}

/**
 * 功能描述: VM中断绑定
 * 入口参数: vm,    VM结构体
 *           id,    中断号
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void interrupts_vm_assign(struct vm *vm, irqid_t id)
{
    if (interrupts_arch_conflict(global_interrupt_bitmap, id)) {
        ERROR("Interrupts conflict, id = %d\n", id);
    }

    interrupts_arch_vm_assign(vm, id);

    bitmap_set(vm->interrupt_bitmap, id);
    bitmap_set(global_interrupt_bitmap, id);
}

/**
 * 功能描述: 判断hypervisor是否注册该中断
 * 入口参数: int_id,    中断号
 * 出口参数: void
 * 返 回 值: bool,      返回对应的中断标志位
 * 修改日期: 2022-09-21
 */
static inline bool interrupt_is_reserved(irqid_t int_id)
{
    return bitmap_get(hyp_interrupt_bitmap, int_id);
}

/**
 * 功能描述: 中断处理函数
 * 入口参数: int_id,    中断号
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
enum irq_res interrupts_handle(irqid_t int_id)
{
    /* 如果VM分配了该中断，注入到VM中 */
    if (vm_has_interrupt(cpu.vcpu->vm, int_id)) {
        interrupts_vm_inject(cpu.vcpu->vm, int_id);
        return FORWARD_TO_VM;
    }
    /* 否则，中断由hypervisor处理 */
    else if (interrupt_is_reserved(int_id)) {
        interrupt_handlers[int_id](int_id);
        return HANDLED_BY_HYP;
    } else {
        ERROR("received unknown interrupt id = %d", int_id);
    }
}